package org.budo.support.ehcache.util;

import java.io.Serializable;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import org.budo.support.lang.util.ReflectUtil;
import org.budo.support.lang.util.StringUtil;
import org.budo.support.spring.context.aware.BudoApplicationContextAware;
import org.springframework.context.ApplicationContext;

import lombok.extern.slf4j.Slf4j;
import net.sf.ehcache.Cache;
import net.sf.ehcache.CacheManager;
import net.sf.ehcache.Element;

/**
 * 缓存工具
 * 
 * @author lmw
 */
@Slf4j
public class EhcacheUtil {
    private static boolean HAS_EHCACHE = ReflectUtil.hasClass("net.sf.ehcache.CacheManager");

    private static CacheManager _cacheManager;

    /**
     * 从缓存中获取
     */
    public static Map<? extends Serializable, ? extends Serializable> cacheGet(CacheManager cacheManager, String cacheName, List<? extends Serializable> cacheKeys) {
        if (null == cacheKeys) {
            log.error("#118 cacheGet, cacheKeys=" + cacheKeys);
            return null;
        }

        if (cacheKeys.isEmpty()) {
            return new HashMap<Serializable, Serializable>();
        }

        Cache cache = getCache(cacheManager, cacheName);
        Map<Serializable, Serializable> map = new HashMap<Serializable, Serializable>();

        for (Serializable key : cacheKeys) {
            Object val = cacheGet(cache, key);

            if (null != val) { // 缓存中有这个
                map.put(key, (Serializable) val);
            }
        }

        return map;
    }

    public static Serializable cacheGet(CacheManager cacheManager, String cacheName, Serializable cacheKey) {
        Cache cache = getCache(cacheManager, cacheName);
        return cacheGet(cache, cacheKey);
    }

    public static Cache getCache(String cacheName) {
        CacheManager cacheManager = getCacheManager();
        return getCache(cacheManager, cacheName);
    }

    public static void cachePut(String cacheName, Serializable cacheKey, Serializable cacheValue) {
        if (!HAS_EHCACHE) {
            log.warn("#69 cachePut, no EHCACHE, cacheName=" + cacheName + ", cacheKey=" + cacheKey + ", cacheValue=" + cacheValue);
            return;
        }

        CacheManager cacheManager = getCacheManager();
        cachePut(cacheManager, cacheName, cacheKey, cacheValue);
    }

    public static Object cacheGet(String cacheName, Serializable cacheKey) {
        if (!HAS_EHCACHE) {
            log.warn("#79 cacheGet, no EHCACHE, cacheName=" + cacheName + ", cacheKey=" + cacheKey);
            return null;
        }

        CacheManager cacheManager = getCacheManager();
        return cacheGet(cacheManager, cacheName, cacheKey);
    }

    public static Boolean cachePut(CacheManager cacheManager, String cacheName, Map<? extends Serializable, ? extends Serializable> map) {
        Cache cache = getCache(cacheManager, cacheName);

        if (null == cache || null == map || map.isEmpty()) {
            log.debug("#60 cachePut return false, cache={}, cacheName={}, map={}", cache, cacheName, map);
            return false;
        }

        for (Entry<? extends Serializable, ? extends Serializable> entry : map.entrySet()) {
            cachePut(cache, entry.getKey(), entry.getValue()); // 逐个放入缓存
        }

        return true;
    }

    public static Cache getCache(CacheManager cacheManager, String cacheName) {
        if (null == cacheManager || StringUtil.isEmpty(cacheName)) {
            log.error("#59 error, cacheManager=" + cacheManager + ", cacheName=" + cacheName);
            return null;
        }

        Cache cache = cacheManager.getCache(cacheName);
        if (null == cache) {
            log.error("#69 error, cacheManager=" + cacheManager + ", cacheName=" + cacheName);
            return null;
        }

        return cache;
    }

    private static Serializable cacheGet(Cache cache, Serializable cacheKey) {
        if (null == cache) {
            log.error("#56 cache is null, cache=" + cache);
            return null;
        }

        Element element = cache.get(cacheKey);

        if (null == element) {
            return null;
        }

        return (Serializable) element.getObjectValue();
    }

    public static Boolean cachePut(Cache cache, Serializable cacheKey, Serializable cacheValue) {
        if (null == cache) {
            log.error("#39 error, cache=" + cache + ", cacheKey=" + cacheKey + ", cacheValue=" + cacheValue);
            return false;
        }

        Element element = new Element(cacheKey, cacheValue);
        cache.put(element);

        return true;
    }

    public static Boolean cachePut(CacheManager cacheManager, String cacheName, Serializable cacheKey, Serializable cacheValue) {
        Cache cache = getCache(cacheManager, cacheName);
        return cachePut(cache, cacheKey, cacheValue);
    }

    public static Boolean cacheRemove(CacheManager cacheManager, String cacheName, Serializable cacheKey) {
        if (null == cacheManager || StringUtil.isEmpty(cacheName) || null == cacheKey) {
            log.error("#19 cacheRemove error, cacheManager=" + cacheManager + ", cacheName=" + cacheName + ", cacheKey=" + cacheKey);
            return false;
        }

        Cache cache = cacheManager.getCache(cacheName);
        if (null == cache) {
            log.error("#26 cache is null, cacheManager=" + cacheManager + ", cacheName=" + cacheName + ", cacheKey=" + cacheKey + ", cache=" + cache);
            return false;
        }

        return cache.remove(cacheKey);
    }

    public static Boolean cacheRemove(String cacheName, String cacheKey) {
        CacheManager cacheManager = getCacheManager();
        return cacheRemove(cacheManager, cacheName, cacheKey);
    }

    public static CacheManager getCacheManager() {
        if (null != _cacheManager) {
            return _cacheManager;
        }

        BudoApplicationContextAware applicationContextAware = BudoApplicationContextAware.getInstanceNoNull();
        ApplicationContext applicationContext = applicationContextAware.getApplicationContext();
        String[] beanNames = applicationContext.getBeanNamesForType(CacheManager.class);
        if (null == beanNames || beanNames.length < 1) {
            log.error("#178 cacheManager not found, beanNames=" + Arrays.toString(beanNames));
            return null;
        }

        if (beanNames.length > 1) {
            log.error("#183 too many cacheManagers, beanNames=" + StringUtil.join(beanNames) //
                    + ", applicationContext=" + applicationContext + ", applicationContextAware=" + applicationContextAware);
        }

        return _cacheManager = (CacheManager) applicationContext.getBean(beanNames[0]);
    }
}